﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Globalization;

namespace Elentok.Common.Wpf.ViewModels.Dialogs
{
  /// <summary>
  /// Defines the view model for the "New Password Dialog"
  /// </summary>
  public interface INewPasswordDialogViewModel : IDialogViewModel
  {
    /// <summary>
    /// Gets or sets the message.
    /// </summary>
    /// <value>The message.</value>
    string Message { get; set; }
    /// <summary>
    /// Gets the password.
    /// </summary>
    /// <value>The password.</value>
    string Password { get; }
    /// <summary>
    /// Gets or sets a value indicating whether empty passwords are allowed.
    /// </summary>
    /// <value><c>true</c> if empty passwords are allowed; otherwise, <c>false</c>.</value>
    bool AllowEmptyPassword { get; set; }
    /// <summary>
    /// Gets the ok command.
    /// </summary>
    /// <value>The ok command.</value>
    ICommand OkCommand { get; }
  }

  /// <summary>
  /// An implementation of the <see cref="INewPasswordDialogViewModel"/> interface.
  /// </summary>
  public class NewPasswordDialogViewModel 
    : DialogViewModelBase, INewPasswordDialogViewModel
  {
    #region Private Members
    private string errorMessage;
    private string message;
    private string password;
    private string password1;
    private string password2;
    private bool allowEmptyPassword;
    #endregion

    /// <summary>
    /// Initializes a new instance of the <see cref="NewPasswordDialogViewModel"/> class.
    /// </summary>
    public NewPasswordDialogViewModel()
    {
      this.Title = "Enter new password";
      this.Message = "Please enter a new password";
      this.OkCommand = new ListeningCommand<object>(this.ExecuteOkCommand, this.CanExecuteOkCommand,
        this, "Password", "ErrorMessage");
    }

    #region INewPasswordDialogViewModel

    /// <summary>
    /// Gets or sets the message
    /// </summary>
    public string Message
    {
      get
      {
        return this.message;
      }
      set
      {
        if (this.message == value)
          return;

        OnPropertyChanging("Message");
        this.message = value;
        OnPropertyChanged("Message");
      }
    }

    /// <summary>
    /// Gets or sets the password
    /// </summary>
    public string Password
    {
      get
      {
        return this.password;
      }
      private set
      {
        if (this.password == value)
          return;

        OnPropertyChanging("Password");
        this.password = value;
        OnPropertyChanged("Password");
      }
    }

    /// <summary>
    /// Gets or sets the allowEmptyPassword
    /// </summary>
    public bool AllowEmptyPassword
    {
      get
      {
        return this.allowEmptyPassword;
      }
      set
      {
        if (this.allowEmptyPassword == value)
          return;

        OnPropertyChanging("AllowEmptyPassword");
        this.allowEmptyPassword = value;
        OnPropertyChanged("AllowEmptyPassword");
      }
    }

    /// <summary>
    /// Gets the ok command.
    /// </summary>
    /// <value>The ok command.</value>
    public ICommand OkCommand { get; private set; }

    #endregion

    /// <summary>
    /// Gets or sets the first entered password.
    /// </summary>
    /// <value>The first entered password.</value>
    public string Password1
    {
      get
      {
        return this.password1;
      }
      set
      {
        if (this.password1 == value)
          return;

        OnPropertyChanging("Password1");
        this.password1 = value;
        UpdatePassword();
        OnPropertyChanged("Password1");
      }
    }

    /// <summary>
    /// Gets or sets the second entered password.
    /// </summary>
    /// <value>The second entered password.</value>
    public string Password2
    {
      get
      {
        return this.password2;
      }
      set
      {
        if (this.password2 == value)
          return;

        OnPropertyChanging("Password2");
        this.password2 = value;
        UpdatePassword();
        OnPropertyChanged("Password2");
      }
    }

    /// <summary>
    /// Gets or sets the error message.
    /// </summary>
    /// <value>The error message.</value>
    public string ErrorMessage
    {
      get
      {
        return this.errorMessage;
      }
      private set
      {
        if (this.errorMessage == value)
          return;

        OnPropertyChanging("ErrorMessage");
        this.errorMessage = value;
        OnPropertyChanged("ErrorMessage");
      }
    }

    private PasswordValidationStatus ValidatePasswords()
    {
      // if the passwords are empty, check if empty passwords are ok
      if (string.IsNullOrEmpty(password1) &&
        string.IsNullOrEmpty(password2))
      {
        if (this.allowEmptyPassword)
          return PasswordValidationStatus.Valid;
        else
          return PasswordValidationStatus.EmptyPasswords;
      }

      // if the passwords are identical, return true
      if (string.Compare(
        password1, password2, false, CultureInfo.InvariantCulture) == 0)
      {
        return PasswordValidationStatus.Valid;
      }

      return PasswordValidationStatus.PasswordsDoNotMatch;
    }

    private void UpdatePassword()
    {
      var status = ValidatePasswords();
      
      if (status == PasswordValidationStatus.Valid)
      {
        this.Password = password1;
        this.ErrorMessage = null;
      }
      else
      {
        this.Password = null;
        if (status == PasswordValidationStatus.PasswordsDoNotMatch)
          this.ErrorMessage = "The passwords do not match";
        else if (status == PasswordValidationStatus.EmptyPasswords)
          this.ErrorMessage = "You must enter a password";
      }
    }

    enum PasswordValidationStatus
    {
      Valid,
      EmptyPasswords,
      PasswordsDoNotMatch
    }

    private void ExecuteOkCommand(object parameter)
    {
      Close(true);
    }

    private bool CanExecuteOkCommand(object parameter)
    {
      return ValidatePasswords() == PasswordValidationStatus.Valid;
    }

    /// <summary>
    /// The execute method of the cancel command
    /// </summary>
    protected override void OnExecuteCancelCommand()
    {
      this.Password = null;

      base.OnExecuteCancelCommand();
    }
  }

}
